<!--
 * @Author: swc shanwenchao12138@163.com
 * @Date: 2025-01-06 10:10:35
 * @LastEditors: zss
 * @LastEditTime: 2025-05-24 17:41:45
 * @FilePath: \my\src\components\custom\myFlow\index.vue
 * @Description: 
 * @Version: 2.0
 * @Autor: swc
-->
<template>
  <div class="init-container">
    <div class="mask-container" v-if="isMask"></div>
    <div :class="['process-flow', isFullScreen ? 'full-screen' : '']">
      <div
        :class="['tooltip', isShowTooltip ? 'tooltip-show' : '']"
        v-show="isFullScreen"
      >
        <div class="tooltip-text">
          <div class="text-item">
            <span class="text-title text-content">{{
              $srv.getI18nByValue('名称')
            }}</span>
            <span class="text-value text-content">{{
              this.craftData['f_name']
                ? $srv.getI18nByValue(this.craftData['f_name'])
                : $srv.getI18nByValue('无')
            }}</span>
          </div>
          <div class="text-item">
            <span class="text-title text-content">{{
              $srv.getI18nByValue('工艺类型')
            }}</span>
            <span class="text-value text-content">{{
              this.craftData['slc|process_type|549166350522667008']
                ? $srv.getI18nBySrv(
                    this.craftData['slc|process_type|549166350522667008']
                  )
                : $srv.getI18nByValue('无')
            }}</span>
          </div>
          <div class="text-item">
            <span class="text-title text-content">{{
              $srv.getI18nByValue('节点数')
            }}</span>
            <span class="text-value text-content">{{ nodesCount }}</span>
          </div>
        </div>
        <div class="tooltip-handle" @click="tooltipHandler"></div>
      </div>
      <!-- 添加悬浮提示框 -->
      <div v-show="tooltip.visible" class="node-tooltip" :style="tooltipStyle">
        <div class="tooltip-title">{{ tooltip.title }}</div>
        <div class="tooltip-section">
          <div class="section-title">工艺槽信息：</div>
          <div v-if="tooltip.tankData.length" class="section-content">
            <div v-for="(tank, index) in tooltip.tankData" :key="index">
              {{ tank.tankName }} - {{ tank.tankNumber }}
            </div>
          </div>
          <div v-else class="section-empty">暂无工艺槽信息</div>
        </div>
        <div class="tooltip-section">
          <div class="section-title">工艺参数：</div>
          <div
            v-if="tooltip.paramData.length"
            class="section-content"
            :class="tooltip.paramData.length >= 10 ? 'twoColumn' : null"
          >
            <div v-for="(param, index) in tooltip.paramData" :key="index">
              {{ param.paramName }}: {{ param.paramValue }}
            </div>
          </div>
          <div v-else class="section-empty">暂无工艺参数</div>
        </div>
      </div>
      <!-- 添加预设节点面板 -->
      <div class="preset-panel">
        <div class="panel-title">预设工序</div>
        <div
          v-for="preset in presetNodes"
          :key="preset.id"
          class="preset-node"
          draggable="true"
          @dragstart="handleDragStart($event, preset)"
        >
          <Icon type="md-document" />
          <span>{{ preset.name }}</span>
        </div>
      </div>

      <!-- 画布 -->
      <div
        id="container"
        ref="container"
        @dragover.prevent
        @drop="handleDrop($event)"
      ></div>

      <!-- 右键菜单 -->
      <div
        v-show="contextMenu.visible"
        :style="contextMenuStyle"
        class="context-menu"
      >
        <ul>
          <template v-if="contextMenu.nodeId">
            <li @click="handleInsertBefore">在此节点前插入</li>
            <li @click="handleInsertAfter">在此节点后插入</li>
            <li @click="handleDeleteNode">删除节点</li>
            <li @click="openProcessDialog(currentNode)">修改工序名称</li>
            <li @click="openTankDialog">修改工序槽体</li>
            <li @click="openParamDialog">修改工序参数</li>
          </template>
          <!-- <li v-else @click="handleAddNode">新增节点</li> -->
          <!-- <li v-if="!contextMenu.nodeId" @click="toggleShowAllParams">
            {{ showAllParams ? '隐藏所有参数' : '显示所有参数' }}
          </li> -->
          <template v-if="!contextMenu.nodeId">
            <li @click="toggleShowAllParams">
              {{ showAllParams ? '隐藏所有参数' : '显示所有参数' }}
            </li>
            <li @click="handleFullScreen">
              {{ isFullScreen ? '缩小' : '全屏' }}
            </li>
          </template>
        </ul>
      </div>
      <!-- <div
        v-for="nodeParams in allNodesParams"
        :key="`params-${nodeParams.id}`"
        :id="`params-${nodeParams.id}`"
        class="node-params-tooltip"
        :style="nodeParams.style"
      >
        <div class="tooltip-title">{{ nodeParams.title }}</div>
        <div class="tooltip-section">
          <div class="section-title">工艺槽信息：</div>
          <div v-if="nodeParams.tankData.length" class="section-content">
            <div v-for="(tank, index) in nodeParams.tankData" :key="index">
              {{ tank.tankName }} - {{ tank.tankNumber }}
            </div>
          </div>
          <div v-else class="section-empty">暂无工艺槽信息</div>
        </div>
        <div class="tooltip-section">
          <div class="section-title">工艺参数：</div>
          <div v-if="nodeParams.paramData.length" class="section-content">
            <div v-for="(param, index) in nodeParams.paramData" :key="index">
              {{ param.paramName }}: {{ param.paramValue }}
            </div>
          </div>
          <div v-else class="section-empty">暂无工艺参数</div>
        </div>
      </div> -->

      <!-- 工序编辑对话框 -->
      <Modal
        v-model="processDialog.visible"
        title="编辑工序信息"
        @on-ok="handleProcessSubmit"
      >
        <Form
          ref="processForm"
          :model="processDialog.form"
          :rules="processDialog.rules"
        >
          <FormItem label="顺序号">
            <Input v-model="processDialog.form.sequence" disabled />
          </FormItem>
          <FormItem label="工序编号" prop="processCode">
            <Input v-model="processDialog.form.processCode" />
          </FormItem>
          <FormItem label="工序名称" prop="processName">
            <Input v-model="processDialog.form.processName" />
          </FormItem>
        </Form>
      </Modal>
    </div>
  </div>
</template>

<script lang="ts">
import { Node } from '@antv/x6';
import {
  ContextMenu,
  defaultNodeConfig,
  Graph,
  I_cmd,
  ifs,
  NodeConfig,
  NodeParamsInfo,
  ParamData,
  ProcessDialog,
  ProcessNodeData,
  T_cmd,
  T_srv,
  TankData,
  TooltipData,
  ts,
  Tsrv,
  vue
} from './config';
import { N_cPflow } from './types';

@T_srv.comp
export default class cPflow extends vue {
  /* 父组件compBoxInfo */
  @T_srv.prop({
    required: false,
    type: Object,
    default: null
  })
  compBoxInfo: ifs.I_compBoxInfo;

  @T_srv.prop({
    required: false,
    type: Object,
    default: null
  })
  config: any;

  /* 组件名 */
  private name: string = 'cPflow';

  private funName: string = '';

  private G_compName: string = this.name;

  /* 组件箱信息对象 */
  private GT_compBoxInfo: ifs.I_compBoxInfo;

  /* 组件箱对象 */
  private GT_compBox: ifs.I_compBox;

  /* 组件箱识别ID */
  private G_compID: string;

  /* 指挥部 */
  private GT_command: I_cmd = new T_cmd();

  /* 遮罩 */
  public isMask: boolean = false;

  private graph: Graph | null = null;

  private contextMenu: ContextMenu = {
    visible: false,
    x: 0,
    y: 0
  };

  private nodeConfig: NodeConfig = defaultNodeConfig;

  private processDialog: ProcessDialog = {
    visible: false,
    form: {
      sequence: 0,
      processCode: '',
      processName: ''
    },
    rules: {
      processCode: [
        { required: true, message: '请输入工序编号', trigger: 'blur' }
      ],
      processName: [
        { required: true, message: '请输入工序名称', trigger: 'blur' }
      ]
    },
    currentNode: null
  };

  // 添加预设节点数据
  private presetNodes = [] as N_cPflow.PresetNode[];
  // private presetNodes = [] as ProcessNodeData[];

  // 工艺ID
  private craftID: string = '';

  private craftType: number = 0;

  private craftData: any = {};

  // 添加 tooltip 数据
  private tooltip: TooltipData = {
    visible: false,
    x: 0,
    y: 0,
    title: '',
    tankData: [],
    paramData: []
  };

  private showAllParams = false;

  private allNodesParams: NodeParamsInfo[] = [];

  readonly carRunSteps: Map<number, any> = new Map();

  private currentNode: Node = null;

  private customMessageConfig: any = null;

  private originNodesData: Map<string, any> = new Map();

  private isFullScreen: boolean = false;

  private showTooltip: boolean = false;

  private currentNodes: Array<Node> = [];

  get isShowTooltip() {
    return this.isFullScreen && this.showTooltip;
  }

  get nodesCount() {
    return this.currentNodes.length;
  }

  created() {
    // 如果父组件没有传参，那就产生一个
    this.GT_compBoxInfo = Tsrv.getCompInfo(this.compBoxInfo);
    this.G_compID = this.GT_compBoxInfo.compID;
    this.GT_compBox = this.GT_compBoxInfo.TcompBox;
    this.GT_command.init(this, this.GT_compBoxInfo);
    this.init();
  }

  mounted() {
    /* 向父组件发送实例 */
    this.GT_compBox.addCompMergeName(this.G_compID, this.G_compName, this);
    this.$emit('compBoxInfo', this.GT_compBoxInfo);
    this.initGraph();
    this.initEvents();
    // 初始化 当前工艺流程的 数据
    this.GT_command.getProcessData();
    window.addEventListener('resize', this.handleResize);
  }

  destroyed() {
    /* 注册组件销毁 */
    this.GT_compBox.removeCompMergeName(this.G_compID, this.G_compName);

    /* 如果父组件传入的compBoxInfo则删除本文件产生的引用,反之是本组件产生的ID统一删除 */
    if (Tsrv.utils.isEmpty(this.compBoxInfo)) {
      this.GT_compBox.removeCompsWithCompID(this.G_compID);
    } else {
      this.GT_compBox.removeCompsMergeName(this.G_compID, [
        `'${this.G_compName}'`
      ]);
    }
    /* 如果是动态组件可能会关闭全局compBox */
    // this.GT_compBox = null

    // 组件关联事件销毁
    if (!Tsrv.noValue(this.customMessageConfig?.custDefListener?.search[0])) {
      if (this.config.listenTYpe === 'default') {
        this.$bus.$off(this.customMessageConfig.custDefListener.search[0]);
      } else {
        this.$mbus.$off(this.customMessageConfig.custDefListener.search[0]);
      }
    }
    // 组件交互事件销毁
    if (
      !Tsrv.noValue(this.customMessageConfig?.custDefListener?.coordination)
    ) {
      this.$bus.$off(this.customMessageConfig.custDefListener.coordination);
    }
  }

  private init() {
    this.funName = 'cPflow'; // 当前配置中的功能名
    // 获取当前工艺ID
    this.getDefListener();
    // 获取预设节点数据
    this.GT_command.getPresetNodeData();
    // 获取行车运行步骤
    this.GT_command.getCarRunSteps();
  }

  /* 设置组件整体遮罩 */
  public setMask(p: boolean) {
    this.isMask = p;
  }

  public getMask(p: boolean): boolean {
    return this.isMask;
  }

  private initGraph(): void {
    if (!this.$refs.container) return;
    const container = this.$refs.container as HTMLElement;

    // 先注册自定义节点
    Graph.registerNode(
      'process-node',
      {
        inherit: 'rect',
        width: 150,
        height: 80,
        markup: [
          {
            tagName: 'rect',
            selector: 'body'
          },
          {
            tagName: 'foreignObject',
            selector: 'tankIcon'
          },
          {
            tagName: 'foreignObject',
            selector: 'paramIcon'
          },
          {
            tagName: 'text',
            selector: 'sequence'
          },
          {
            tagName: 'text',
            selector: 'processName'
          },
          {
            tagName: 'text',
            selector: 'processCode'
          },
          {
            tagName: 'path',
            selector: 'divider'
          },
          {
            tagName: 'foreignObject',
            selector: 'paramArea'
          }
        ],
        attrs: {
          body: {
            fill: '#fff',
            stroke: '#1890ff',
            strokeWidth: 1,
            rx: 4,
            ry: 4
          },
          tankIcon: {
            ref: 'body',
            refX: 10,
            refY: 10,
            width: 16,
            height: 16,
            html:
              '<i class="ivu-icon ivu-icon-md-cube" style="font-size: 16px; color: #1890ff; cursor: pointer;" data-icon="tank"></i>'
          },
          paramIcon: {
            ref: 'body',
            refX: 30,
            refY: 10,
            width: 16,
            height: 16,
            html:
              '<i class="ivu-icon ivu-icon-md-settings" style="font-size: 16px; color: #1890ff; cursor: pointer;" data-icon="param"></i>'
          },
          sequence: {
            ref: 'body',
            refX: '50%',
            refY: 25,
            fontSize: 12,
            fill: '#666',
            textAnchor: 'middle'
          },
          processName: {
            ref: 'body',
            refX: '50%',
            refY: 45,
            fontSize: 14,
            fill: '#333',
            textAnchor: 'middle'
          },
          processCode: {
            ref: 'body',
            refX: '50%',
            refY: 65,
            fontSize: 12,
            fill: '#666',
            textAnchor: 'middle'
          }
        },
        ports: {
          // groups: {
          //   in: {
          //     position: 'left',
          //     attrs: {
          //       circle: {
          //         r: 4,
          //         magnet: true,
          //         stroke: '#1890ff',
          //         fill: '#fff',
          //         strokeWidth: 1
          //       }
          //     }
          //   },
          //   out: {
          //     position: 'right',
          //     attrs: {
          //       circle: {
          //         r: 4,
          //         magnet: true,
          //         stroke: '#1890ff',
          //         fill: '#fff',
          //         strokeWidth: 1
          //       }
          //     }
          //   }
          // },
          // items: [
          //   { group: 'in', id: 'in' },
          //   { group: 'out', id: 'out' }
          // ]
        }
      },
      true
    );

    // 创建图实例
    this.graph = new Graph({
      container,
      width: container.clientWidth,
      height: container.clientHeight,
      scaling: {
        min: 0.5,
        max: 1.5
      },
      grid: {
        visible: true,
        type: 'dot',
        size: 10,
        args: {
          color: '#E2E2E2',
          thickness: 1
        }
      },
      connecting: {
        snap: true,
        allowBlank: false,
        allowLoop: false,
        highlight: true,
        connector: 'smooth',
        validateConnection: ({ sourceCell, targetCell }) => {
          if (sourceCell && targetCell) {
            const sourceData = sourceCell.getData() as ProcessNodeData;
            const targetData = targetCell.getData() as ProcessNodeData;
            return targetData.sequence - sourceData.sequence === 1;
          }
          return false;
        }
      },
      interacting: {
        nodeMovable: false,
        edgeMovable: false,
        edgeLabelMovable: false
      },
      background: {
        color: '#F8F9FA'
      },
      mousewheel: {
        enabled: true,
        modifiers: ['ctrl', 'meta']
      }
    });
  }

  private initEvents(): void {
    if (!this.graph) return;

    // 右键菜单事件
    this.graph.on('node:contextmenu', ({ e, node }) => {
      e.preventDefault();
      this.showContextMenu(e.clientX, e.clientY, node.id);
      this.currentNode = this.graph.getCellById(
        this.contextMenu.nodeId
      ) as Node;
    });

    this.graph.on('blank:contextmenu', ({ e }) => {
      this.currentNode = null;
      e.preventDefault();
      this.showContextMenu(e.clientX, e.clientY);
    });

    // 双击编辑事件
    this.graph.on('node:dblclick', ({ node }) => {
      this.openProcessDialog(node);
    });

    // 修改图标点击事件处理
    this.graph.on('cell:click', ({ e, cell }) => {
      if (!cell.isNode()) return;
      this.currentNode = cell as Node;

      const target = e.target as HTMLElement;
      // 检查点击的是否是图标或其父元素
      const isTankIcon = target.closest('.ivu-icon-md-cube');
      const isParamIcon = target.closest('.ivu-icon-md-settings');

      if (isTankIcon) {
        this.openTankDialog();
      } else if (isParamIcon) {
        this.openParamDialog();
      }
    });

    // 点击空白处关闭右键菜单和所有参数显示
    this.graph!.on('blank:click', () => {
      this.contextMenu.visible = false;
      this.currentNode = null;
      // this.showAllParams = false;
      this.allNodesParams = [];
    });

    // 添加鼠标悬停事件
    this.graph.on('node:mouseenter', ({ node, e }) => {
      const data = node.getData() as ProcessNodeData;
      const rect = (e.target as HTMLElement).getBoundingClientRect();

      this.tooltip = {
        visible: true,
        x: rect.right + 10,
        y: rect.top,
        title: `${data.processName} (#${data.sequence})`,
        tankData: data.tankData,
        paramData: data.paramData
      };
    });

    this.graph.on('node:mouseleave', () => {
      this.tooltip.visible = false;
    });

    // 节点位置变化时更新参数显示位置
    this.graph!.on('node:change:position', () => {
      if (this.showAllParams) {
        this.updateAllNodesParams();
      }
    });
  }

  private get contextMenuStyle() {
    return {
      left: `${this.contextMenu.x}px`,
      top: `${this.contextMenu.y}px`
    };
  }

  private showContextMenu(x: number, y: number, nodeId?: string): void {
    this.contextMenu = {
      visible: true,
      x,
      y,
      nodeId
    };
  }

  private getDefListener() {
    this.customMessageConfig = {
      custDefListener: {
        coordination: '6ABD67A4AAF9F2AA',
        search: ['5438F55E67F8CF7F']
      },
      emitActions: {
        search: [
          {
            toID: '4F78AAB5BD4060D7',
            msg: {
              type: 'search',
              content: {
                ikey: 'id',
                ukey: 'master_bill_id',
                val: ''
              }
            },
            sourceToID: 'process_specified_slot'
          },
          {
            toID: 'D8E6DD0CA8A044F7',
            msg: {
              type: 'search',
              content: {
                ikey: 'id',
                ukey: 'master_bill_id',
                val: ''
              }
            },
            sourceToID: 'process_para_details'
          }
        ],
        refresh: []
      },
      eventGroup: {
        '': {
          custDefListener: {
            coordination: '4C58B4C26BA33D78'
          },
          children: [
            {
              process_mgmt: {
                custDefListener: {
                  coordination: '7A1749F7BCC4F4BE'
                },
                children: [
                  {
                    process_info: {
                      custDefListener: {
                        coordination: '6ABD67A4AAF9F2AA'
                      },
                      children: [
                        {
                          process_specified_slot: {
                            custDefListener: {
                              coordination: 'D92DD731BF6C8E08'
                            },
                            children: []
                          }
                        },
                        {
                          process_para_details: {
                            custDefListener: {
                              coordination: '6A0A6EDE236F43EB'
                            },
                            children: []
                          }
                        }
                      ]
                    }
                  }
                ]
              }
            }
          ]
        }
      }
    };
    const coordination = {
      name: 'process_info',
      messageConfig: this.customMessageConfig
    };
    this.GT_command.newTcompCoordination(coordination);

    // 事件监听（联动过滤）--
    if (
      !Tsrv.utils.noValue(this.customMessageConfig?.custDefListener?.search)
    ) {
      if (this.config.listenType === 'default') {
        this.$bus.$on(
          this.customMessageConfig?.custDefListener.search[0],
          (m: any) => {
            this.receiveEvent(m);
          }
        );
      } else {
        this.$mbus.$on(
          this.customMessageConfig?.custDefListener.search[0],
          (m: any) => {
            this.receiveEvent(m);
          }
        );
      }
    }

    // 蒙层--
    if (this.customMessageConfig?.custDefListener?.coordination) {
      this.$bus.$on(
        this.customMessageConfig.custDefListener.coordination,
        msg => {
          this.GT_command.receEventCoordination(msg);
        }
      );
    }
  }

  private receiveEvent(m: any) {
    this.craftID = m?.option?.autoCompleteInfo?.data[m.content.ikey] || '';
    this.craftData = m?.option?.autoCompleteInfo?.data || {};
    if (!Tsrv.noValue(m?.option?.autoCompleteInfo?.data['process_type'])) {
      this.craftType = m?.option?.autoCompleteInfo?.data['process_type'];
    }
    this.GT_command.getProcessData();
  }

  private handleAddNode(presetNode?: N_cPflow.PresetNode): void {
    const presetData = presetNode.data as ProcessNodeData;
    if (!this.graph) return;

    const nodes = this.graph.getNodes();
    const sequence = nodes.length + 1;

    const node = this.graph.addNode({
      shape: 'process-node',
      data:
        presetData &&
        Object.prototype.hasOwnProperty.call(presetData, 'sequence')
          ? {
              ...presetData,
              sequence,
              id: Tsrv.getSnowIDFmtStr()
            }
          : ({
              id: Tsrv.getSnowIDFmtStr(),
              sequence,
              processId: '',
              processCode: '',
              processName: '',
              tankData: [] as TankData[],
              paramData: [] as ParamData[]
            } as ProcessNodeData),
      position: this.calculateNodePosition(sequence),
      attrs: {
        sequence: { text: `#${sequence}` },
        processName: { text: presetData?.processName || '未命名工序' },
        processCode: { text: presetData?.processCode || '无编号' },
        tankIcon: {
          html: `<i class="ivu-icon ivu-icon-md-cube" style="font-size: 16px; color: ${
            presetData?.tankData?.length ? '#1890ff' : '#999'
          }; cursor: pointer;" data-icon="tank"></i>`
        },
        paramIcon: {
          html: `<i class="ivu-icon ivu-icon-md-settings" style="font-size: 16px; color: ${
            presetData?.paramData?.length ? '#1890ff' : '#999'
          }; cursor: pointer;" data-icon="param"></i>`
        }
      }
    });
    const newNodeData: ProcessNodeData = node.getData();
    this.graph.updateCellId(node, newNodeData.id);

    // if (nodes.length > 0) {
    //   const sourceNode = nodes[nodes.length - 1];
    //   this.graph.addEdge({
    //     source: sourceNode,
    //     target: node,
    //     connector: 'smooth',
    //     attrs: {
    //       line: {
    //         stroke: '#1890ff',
    //         strokeWidth: 1
    //       }
    //     }
    //   });
    // }

    this.afterInsertNode((node as unknown) as Node)
      .then(resp => {
        if (!resp) {
          // 删除节点
          this.graph.removeNode(newNodeData.id);
        }
        this.reorderNodes();
      })
      .catch(error => {
        this.graph.removeNode(node);
        this.reorderNodes();
      });

    this.contextMenu.visible = false;
  }

  private handleDeleteNode(): void {
    if (!this.graph || !this.contextMenu.nodeId) return;

    const node = this.graph.getCellById(this.contextMenu.nodeId) as Node;
    if (node) {
      this.GT_command.deleteNode(node).then(resp => {
        if (resp) {
          this.graph.removeCell(node);
          this.reorderNodes();
        }
      });
    }
    this.contextMenu.visible = false;
  }

  private calculateNodePosition(sequence: number): { x: number; y: number } {
    const { width, height, rowSpacing, columnSpacing } = this.nodeConfig;
    const containerWidth =
      // (this.$refs.container as HTMLElement).clientWidth - 100;
      (this.$refs.container as HTMLElement).clientWidth - 102;

    // 根据是否显示参数来调整节点宽度
    const nodeWidth = this.showAllParams ? 300 : width;
    const nodeHeight = this.showAllParams ? 160 : height;

    // 计算每行可以放置的节点数
    const nodesPerRow = Math.floor(
      containerWidth / (nodeWidth + columnSpacing)
    );

    const row = Math.floor((sequence - 1) / nodesPerRow);
    const col = (sequence - 1) % nodesPerRow;

    // S形布局
    const x =
      row % 2 === 0
        ? col * (nodeWidth + columnSpacing) + 50
        : (nodesPerRow - 1 - col) * (nodeWidth + columnSpacing) + 50;

    // 使用实际节点高度和行间距计算垂直位置
    const y = row * (nodeHeight + rowSpacing) + 50;

    return { x, y };
  }

  /**
   * @description: 重新排列节点
   * @return {*}
   * @author: swc
   * @Date: 2025-01-07 09:34:26
   * @LastEditors: swc
   * @LastEditTime: Do not edit
   */
  private reorderNodes(): void {
    if (!this.graph) return;

    const nodes = this.graph.getNodes();
    const edges = this.graph.getEdges();

    // 清除所有连线
    edges.forEach(edge => edge.remove());

    this.currentNodes = Tsrv.utils.clone(nodes, true);

    // 新排序和连线
    nodes.forEach((node, index) => {
      const newSequence = index + 1;

      // 更新节点数据
      const data = node.getData() as ProcessNodeData;
      data.sequence = newSequence;
      node.setData(data);

      // 更新节点显示
      node.setAttrs({
        sequence: { text: `#${newSequence}` },
        processName: { text: data.processName || '未命名工序' },
        processCode: { text: data.processCode || '无编号' },
        tankIcon: {
          html: `<i class="ivu-icon ivu-icon-md-cube" style="font-size: 16px; color: ${
            data.tankData?.length ? '#1890ff' : '#999'
          }; cursor: pointer;" data-icon="tank"></i>`
        },
        paramIcon: {
          html: `<i class="ivu-icon ivu-icon-md-settings" style="font-size: 16px; color: ${
            data.paramData?.length ? '#1890ff' : '#999'
          }; cursor: pointer;" data-icon="param"></i>`
        }
      });

      // 更新位置
      const position = this.calculateNodePosition(newSequence);
      node.setPosition(position);

      // 添加连线
      if (index < nodes.length - 1) {
        this.graph!.addEdge({
          source: node,
          target: nodes[index + 1],
          connector: 'smooth',
          attrs: {
            line: {
              stroke: '#1890ff',
              strokeWidth: 1
            }
          }
        });
      }
    });

    this.afterReorderNodes();

    // 如果正在显示参数，更新参数显示
    if (this.showAllParams) {
      this.$nextTick(() => {
        this.updateAllNodesParams();
      });
    }
  }

  // 导出数据的方法
  public getAllNodesData(): ProcessNodeData[] {
    if (!this.graph) return [];

    return this.graph.getNodes().map(node => {
      return node.getData() as ProcessNodeData;
    });
  }

  private openProcessDialog(node: Node): void {
    this.contextMenu.visible = false;
    let data = null;
    if (node) {
      data = node.getData() as ProcessNodeData;
    } else {
      data = this.graph.getCellById(this.contextMenu.nodeId).getData();
    }
    // const data = node.getData() as ProcessNodeData;
    this.processDialog = {
      ...this.processDialog,
      visible: true,
      currentNode: node as Node,
      form: {
        sequence: data.sequence,
        processCode: data.processCode,
        processName: data.processName
      }
    };
  }

  private handleProcessSubmit(): void {
    if (!this.processDialog.currentNode) return;

    // 验证表单
    if (
      !this.processDialog.form.processCode ||
      !this.processDialog.form.processName
    ) {
      this.$Message.error('工序编号和名称不能为空');
      return;
    }

    const node = (this.processDialog.currentNode as unknown) as Node;
    const data = node.getData() as ProcessNodeData;
    // 更新节点数据
    const updateRecords = [];
    const rows = [
      new ts.T_fv().setField('id').setValue(data.id),
      new ts.T_fv()
        .setField('process_alias')
        .setValue(this.processDialog.form.processName),
      new ts.T_fv()
        .setField('manuf_operation_num')
        .setValue(this.processDialog.form.processCode)
    ] as ifs.I_fv[];
    updateRecords.push(rows);
    this.GT_command.updateData('process_info', updateRecords).then(resp => {
      // 先存表后更新节点
      if (resp) {
        Object.assign(data, {
          processCode: this.processDialog.form.processCode,
          processName: this.processDialog.form.processName
        });

        node.setData(data);

        // 更新节点显示
        node.setAttrs({
          sequence: {
            text: `#${data.sequence}`
          },
          processName: {
            text: data.processName
          },
          processCode: {
            text: data.processCode
          }
        });
      }
    });
  }

  private afterInsertNode(newNode: Node): Promise<boolean> {
    // 无工序ID无效数据，禁止插入
    return new Promise(resolve => {
      const nodeData = newNode.getData() as ProcessNodeData;
      if (!this.craftID || !nodeData.id || !nodeData.processId) resolve(false);
      const tableName = 'process_info';
      const insertRecords = [];
      const rows = [
        new ts.T_fv()
          .setField(Tsrv.globalVar.fieldName.ID)
          .setValue(nodeData.id),
        new ts.T_fv().setField('master_bill_id').setValue(this.craftID),
        // new ts.T_fv()
        //   .setField('sequential_id')
        //   .setValue(this.carRunSteps.get(nodeData.sequence).sequenceId), // 序号
        new ts.T_fv()
          .setField('manuf_operation_id')
          .setValue(nodeData.processId) // 工序ID
      ];
      insertRecords.push(rows);
      const bDatas: ifs.I_baseData[] = [];
      insertRecords.forEach(ele => {
        const bData = new ts.T_baseData();
        bData
          .setDataRule({} as ifs.I_rule)
          .setFileInfo({})
          .setRowData(...(ele as ifs.I_fv[]));
        bDatas.push(bData);
      });
      this.GT_command.insertData(tableName, bDatas)
        .then(resp => {
          if (resp) {
            resolve(true);
          } else {
            resolve(false);
          }
        })
        .catch(err => {
          resolve(false);
        });
    });
  }

  /**
   * @description: 排序完成后，更新工序表（节点）中的顺序号
   * @return {*}
   * @author: swc
   * @Date: 2025-01-10 18:54:29
   * @LastEditors: swc
   * @LastEditTime: Do not edit
   */
  private afterReorderNodes(): Promise<boolean> {
    return new Promise(resolve => {
      const tableName = 'process_info';
      const updateRecords = [];
      const nodes = this.graph?.getNodes();
      const processData = this.GT_command.processData;
      // 旧数据序号若有变化，则更新
      processData.forEach((process, processId) => {
        const index = nodes.findIndex(it => it.id === processId);
        const proData = process.data as ProcessNodeData;
        if (index > -1 && proData) {
          const nodeData = nodes[index].getData() as ProcessNodeData;
          if (proData.sequence !== nodeData.sequence) {
            const rows = [
              new ts.T_fv()
                .setField(Tsrv.globalVar.fieldName.ID)
                .setValue(processId),
              new ts.T_fv()
                .setField('sequential_id')
                .setValue(this.carRunSteps.get(nodeData.sequence)?.sequenceId) // 序号
            ];
            updateRecords.push(rows);
          }
        }
      });
      // 新数据全部更新
      nodes.forEach((node, index) => {
        if (!processData.has(node.id)) {
          const nodeData = node.getData() as ProcessNodeData;
          const rows = [
            new ts.T_fv()
              .setField(Tsrv.globalVar.fieldName.ID)
              .setValue(nodeData.id),
            new ts.T_fv()
              .setField('sequential_id')
              .setValue(this.carRunSteps.get(nodeData.sequence)?.sequenceId) // 序号
          ];
          updateRecords.push(rows);
        }
      });
      if (updateRecords.length > 0) {
        this.GT_command.updateData(tableName, updateRecords)
          .then(resp => {
            if (resp) {
              resolve(true);
            } else {
              resolve(false);
            }
          })
          .catch(err => {
            resolve(false);
          });
      }
    });
  }

  private validateNodeData(node: Node): boolean {
    const data = node.getData() as ProcessNodeData;
    if (!data) return false;

    // 确保数据结构完整
    if (!Array.isArray(data.tankData)) {
      data.tankData = [];
    }
    if (!Array.isArray(data.paramData)) {
      data.paramData = [];
    }
    node.setData(data);
    return true;
  }

  private openTankDialog(): void {
    this.contextMenu.visible = false;
    let allow = true;
    let data = null;
    if (!this.currentNode) {
      data = this.graph.getCellById(this.contextMenu.nodeId).getData();
      if (!data.processId) {
        allow = false;
      } else {
        this.currentNode = this.graph.getCellById(
          this.contextMenu.nodeId
        ) as Node;
      }
    } else {
      data = this.currentNode.getData() as ProcessNodeData;
      if (!data.processId) allow = false;
    }
    if (allow) {
      this.validateNodeData(this.currentNode);
      // const dialog = this.$refs.tankDialog as InstanceType<typeof TankDialog>;
      // dialog.open(node);
      Tsrv.utils
        .createModal('c-tank-dialog', {
          node: this.currentNode,
          craftType: this.craftType
        })
        .then(resp => {});
    }
  }

  private openParamDialog(): void {
    this.contextMenu.visible = false;
    let allow = true;
    let data = null;
    if (!this.currentNode) {
      data = this.graph.getCellById(this.contextMenu.nodeId).getData();
      if (data.tankData.length === 0) {
        allow = false;
      } else {
        this.currentNode = this.graph.getCellById(
          this.contextMenu.nodeId
        ) as Node;
      }
    } else {
      data = this.currentNode.getData() as ProcessNodeData;
      if (data.tankData.length === 0) allow = false;
    }
    if (allow) {
      this.validateNodeData(this.currentNode);
      // const dialog = this.$refs.paramDialog as InstanceType<typeof ParamDialog>;
      // dialog.open(node);
      Tsrv.utils
        .createModal('c-param-dialog', { node: this.currentNode })
        .then(resp => {});
    } else {
      // 请先添加槽体
      this.$Message.warning('请先添加槽体');
    }
  }

  // 拖拽开始时保存被拖拽的预设节点数据
  private handleDragStart(e: DragEvent, preset: N_cPflow.PresetNode): void {
    e.dataTransfer?.setData('preset-id', preset.id);
  }

  // 处理拖拽放置
  private handleDrop(e: DragEvent): void {
    if (!this.graph) return;

    const presetId = e.dataTransfer?.getData('preset-id');
    if (!presetId) return;

    const preset = this.presetNodes.find(p => p.id === presetId);
    if (!preset) return;

    // 检查是否拖到了节点上
    const target = e.target as HTMLElement;
    const nodeEl = target.closest('.x6-node');

    if (nodeEl) {
      const nodeId = nodeEl.getAttribute('data-cell-id');
      if (nodeId) {
        const targetNode = this.graph.getCellById(nodeId) as Node;
        if (targetNode) {
          const rect = nodeEl.getBoundingClientRect();
          const isUpper = e.clientY < rect.top + rect.height / 2;

          // 在目标节点前后插入
          const position = targetNode.getPosition();
          this.insertNode(position, nodeId, isUpper, preset.data);
          return;
        }
      }
    }

    // 如果不是拖到节点上，则添加到末尾
    this.handleAddNode(preset);
  }

  // 修改 insertNode 方法以支持预设数据
  private insertNode(
    position: { x: number; y: number },
    targetNodeId: string,
    insertBefore: boolean,
    presetData?: ProcessNodeData
  ): void {
    if (!this.graph) return;

    const nodes = this.graph.getNodes();
    const targetIndex = nodes.findIndex(node => node.id === targetNodeId);
    if (targetIndex === -1) return;

    // 创建新节点
    const presetNode = {
      shape: 'process-node',
      data: presetData
        ? {
            ...presetData,
            sequence: 0,
            id: Tsrv.getSnowIDFmtStr()
          }
        : ({
            id: Tsrv.getSnowIDFmtStr(),
            sequence: 0,
            processId: '',
            processCode: '',
            processName: '',
            tankData: [] as TankData[],
            paramData: [] as ParamData[]
          } as ProcessNodeData),
      position: {
        // 测试是否重复？（reorderNodes）
        x: position.x,
        y: position.y + (insertBefore ? -100 : 100)
      },
      attrs: {
        // 测试是否重复？（reorderNodes）
        sequence: { text: '' },
        processName: { text: presetData?.processName || '未命名工序' },
        processCode: { text: presetData?.processCode || '无编号' },
        tankIcon: {
          html: `<i class="ivu-icon ivu-icon-md-cube" style="font-size: 16px; color: ${
            presetData?.tankData?.length ? '#1890ff' : '#999'
          }; cursor: pointer;" data-icon="tank"></i>`
        },
        paramIcon: {
          html: `<i class="ivu-icon ivu-icon-md-settings" style="font-size: 16px; color: ${
            presetData?.paramData?.length ? '#1890ff' : '#999'
          }; cursor: pointer;" data-icon="param"></i>`
        }
      }
    };
    const newNode = this.graph.addNode(presetNode);

    // 重新排序节点数组
    const updatedNodes = [...nodes];
    const insertIndex = insertBefore ? targetIndex : targetIndex + 1;
    updatedNodes.splice(insertIndex, 0, newNode);

    // 更新所有节点的序号和位置
    updatedNodes.forEach((node, index) => {
      const data = node.getData() as ProcessNodeData;
      data.sequence = index + 1; // 测试是否重复？（reorderNodes）
      node.setData(data);

      // 更新节点显示
      node.setAttrs({
        sequence: { text: `#${index + 1}` },
        processName: { text: data.processName || '未命名工序' }, // 测试是否重复？（reorderNodes）
        processCode: { text: data.processCode || '无编号' } // 测试是否重复？（reorderNodes）
      });

      // 更新位置
      const newPosition = this.calculateNodePosition(index + 1); // 测试是否重复？（reorderNodes）
      node.setPosition(newPosition); // 测试是否重复？（reorderNodes）
    });

    // 清除所有现有连线
    // this.graph.getEdges().forEach(edge => edge.remove());

    // 目的：解决排序问题（解决新建节点尾部累加，而不是指定位置添加）
    this.graph.clearCells();
    updatedNodes.forEach((node, index) => {
      const newN = this.graph.addNode(node);
      const newNData = newN.getData() as ProcessNodeData;
      this.graph.updateCellId(newN, newNData.id);
    });

    // 重新创建连线
    // const newNodes = this.graph.getNodes();
    // newNodes.forEach((node, index) => {
    //   if (index < newNodes.length - 1) {
    //     this.graph!.addEdge({
    //       source: node,
    //       target: newNodes[index + 1],
    //       connector: 'smooth',
    //       attrs: {
    //         line: {
    //           stroke: '#1890ff',
    //           strokeWidth: 1
    //         }
    //       }
    //     });
    //   }
    // });

    this.afterInsertNode((newNode as unknown) as Node)
      .then(resp => {
        if (!resp) {
          // 删除节点
          this.graph.removeNode(newNode);
        }
        this.reorderNodes();
      })
      .catch(error => {
        this.graph.removeNode(newNode);
        this.reorderNodes();
      });

    // 更新参数显示
    if (this.showAllParams) {
      this.$nextTick(() => {
        this.updateAllNodesParams();
      });
    }
  }

  // 组件销毁时清理事件监听
  beforeDestroy() {
    document.removeEventListener('click', () => {
      this.contextMenu.visible = false;
    });

    if (this.graph) {
      this.graph.dispose();
    }
    window.removeEventListener('resize', this.handleResize);
  }

  // 添加 tooltip 样式计算属性
  private get tooltipStyle() {
    return {
      left: `${this.tooltip.x}px`,
      top: `${this.tooltip.y}px`
    };
  }

  /**
   * @description: 切换node显示模式
   * @return {*}
   * @author: swc
   * @Date: 2025-01-07 09:51:03
   * @LastEditors: swc
   * @LastEditTime: Do not edit
   */
  private toggleShowAllParams(): void {
    this.showAllParams = !this.showAllParams;
    this.contextMenu.visible = false;

    if (!this.graph) return;

    const nodes = this.graph.getNodes();

    if (this.showAllParams) {
      // 先调整节点间距
      this.nodeConfig.rowSpacing = 80;
      this.nodeConfig.columnSpacing = 60;

      // 展开节点显示参数
      nodes.forEach(node => {
        const data = node.getData() as ProcessNodeData;
        node.resize(300, 160);

        // 更新节点样式
        node.setAttrs({
          body: {
            fill: '#f8f9fa',
            strokeWidth: 1,
            stroke: '#1890ff'
          },
          sequence: {
            refY: 20,
            fontSize: 12
          },
          processName: {
            refY: 40,
            fontSize: 14
          },
          processCode: {
            refY: 60,
            fontSize: 12
          },
          divider: {
            tagName: 'path',
            d: 'M 0 80 L 300 80',
            stroke: '#e8e8e8',
            strokeWidth: 1
          },
          paramArea: {
            tagName: 'foreignObject',
            x: 10,
            y: 90,
            width: 280,
            height: 60,
            html: this.generateParamHtml(data)
          }
        });
        // 修改节点markup （修）
        node.setMarkup([
          {
            tagName: 'rect',
            selector: 'body'
          },
          {
            tagName: 'foreignObject',
            selector: 'tankIcon'
          },
          {
            tagName: 'foreignObject',
            selector: 'paramIcon'
          },
          {
            tagName: 'text',
            selector: 'sequence'
          },
          {
            tagName: 'text',
            selector: 'processName'
          },
          {
            tagName: 'text',
            selector: 'processCode'
          },
          {
            tagName: 'path',
            selector: 'divider'
          },
          {
            tagName: 'foreignObject',
            selector: 'paramArea'
          }
        ]);
      });
    } else {
      // 恢复默认间距
      this.nodeConfig.rowSpacing = defaultNodeConfig.rowSpacing;
      this.nodeConfig.columnSpacing = defaultNodeConfig.columnSpacing;

      // 恢复节点原始大小和样式
      nodes.forEach(node => {
        const data = node.getData() as ProcessNodeData;
        node.resize(150, 80);

        // 完全重置节点样式
        node.setAttrs({
          body: {
            fill: '#fff',
            strokeWidth: 1,
            stroke: '#1890ff'
          },
          sequence: {
            refY: 25,
            fontSize: 12,
            text: `#${data.sequence}`
          },
          processName: {
            refY: 45,
            fontSize: 14,
            text: data.processName || '未命名工序'
          },
          processCode: {
            refY: 65,
            fontSize: 12,
            text: data.processCode || '无编号'
          },
          tankIcon: {
            html: `<i class="ivu-icon ivu-icon-md-cube" style="font-size: 16px; color: ${
              data.tankData?.length ? '#1890ff' : '#999'
            }; cursor: pointer;" data-icon="tank"></i>`
          },
          paramIcon: {
            html: `<i class="ivu-icon ivu-icon-md-settings" style="font-size: 16px; color: ${
              data.paramData?.length ? '#1890ff' : '#999'
            }; cursor: pointer;" data-icon="param"></i>`
          },
          // 完全移除额外的元素，而不是仅仅隐藏
          divider: null,
          paramArea: null
        });
        // 修改节点markup （修）
        node.setMarkup([
          {
            tagName: 'rect',
            selector: 'body'
          },
          {
            tagName: 'foreignObject',
            selector: 'tankIcon'
          },
          {
            tagName: 'foreignObject',
            selector: 'paramIcon'
          },
          {
            tagName: 'text',
            selector: 'sequence'
          },
          {
            tagName: 'text',
            selector: 'processName'
          },
          {
            tagName: 'text',
            selector: 'processCode'
          }
        ]);
      });

      // 清除所有参数显示
      this.allNodesParams = [];
    }

    // 重新排列节点位置
    this.reorderNodes();
  }

  // 生成参数显示的HTML
  private generateParamHtml(data: ProcessNodeData): string {
    const tankHtml = data.tankData?.length
      ? data.tankData
          .map(
            t =>
              `<div class="param-item">📦 ${t.tankName}: ${t.tankNumber}</div>`
          )
          .join('')
      : '<div class="param-empty">暂无工艺槽信息</div>';

    const paramHtml = data.paramData?.length
      ? data.paramData
          .map(
            p =>
              `<div class="param-item">⚙️ ${p.paramName}: ${p.paramValue}</div>`
          )
          .join('')
      : '<div class="param-empty">暂无工艺参数</div>';

    return `
      <div class="y-scroll" style="font-size: 12px; color: #666; padding: 0 5px; height: 100%;">
        <div style="margin-bottom: 5px;">
          ${tankHtml}
        </div>
        <div>
          ${paramHtml}
        </div>
      </div>
    `;
  }

  private updateAllNodesParams(): void {
    if (!this.graph) return;

    const nodes = this.graph.getNodes();

    // 先清空现有参数显示
    this.allNodesParams = [];

    // 计算每个节点的参数显示信息
    nodes.forEach(node => {
      const data = node.getData() as ProcessNodeData;
      const position = node.getPosition();
      const size = node.getSize();

      this.allNodesParams.push({
        id: `${node.id}-params`,
        visible: true,
        x: position.x,
        y: position.y,
        title: `${data.processName} (#${data.sequence})`,
        tankData: data.tankData || [],
        paramData: data.paramData || [],
        style: {
          position: 'absolute',
          left: `${position.x}px`,
          top: `${position.y + size.height + 20}px`,
          width: `${size.width}px`
        }
      });
    });

    // 等待 DOM 更新后调整节点间距
    this.$nextTick(() => {
      const maxParamHeight = Math.max(
        ...this.allNodesParams.map(param => {
          const el = document.getElementById(`params-${param.id}`);
          return el ? el.offsetHeight : 0;
        })
      );

      if (maxParamHeight > 0) {
        this.nodeConfig.rowSpacing = maxParamHeight + 60;
        // this.reorderNodes();
      }
    });
  }

  private handleResize = () => {
    if (this.showAllParams) {
      this.updateAllNodesParams();
    }
  };

  private handleInsertBefore(): void {
    if (!this.graph || !this.contextMenu.nodeId) return;

    const targetNode = this.graph.getCellById(this.contextMenu.nodeId) as Node;
    if (!targetNode) return;

    const position = targetNode.getPosition();
    this.insertNode(position, this.contextMenu.nodeId, true);
    this.contextMenu.visible = false;
  }

  private handleInsertAfter(): void {
    if (!this.graph || !this.contextMenu.nodeId) return;

    const targetNode = this.graph.getCellById(this.contextMenu.nodeId) as Node;
    if (!targetNode) return;

    const position = targetNode.getPosition();
    this.insertNode(position, this.contextMenu.nodeId, false);
    this.contextMenu.visible = false;
  }

  public handleFullScreen() {
    this.isFullScreen = !this.isFullScreen;
    this.contextMenu.visible = false;
    this.currentNode = null;
    this.allNodesParams = [];
  }

  private tooltipHandler() {
    this.showTooltip = !this.showTooltip;
  }
}
</script>
<style lang="scss" scoped>
.process-flow {
  width: 100%;
  height: 100%;
  position: relative;
  display: flex;

  .preset-panel {
    width: 200px;
    height: 100%;
    border-right: 1px solid #e8e8e8;
    background: #fff;
    padding: 16px;
    box-sizing: border-box;

    .panel-title {
      font-size: 16px;
      font-weight: 500;
      margin-bottom: 16px;
      color: #333;
    }

    .preset-node {
      padding: 8px 12px;
      border: 1px solid #e8e8e8;
      border-radius: 4px;
      margin-bottom: 8px;
      cursor: move;
      display: flex;
      align-items: center;
      transition: all 0.3s;

      &:hover {
        background: #f5f5f5;
        border-color: #1890ff;
      }

      .ivu-icon {
        margin-right: 8px;
        color: #1890ff;
      }
    }
  }

  #container {
    flex: 1;
    height: 100%;
    padding: 50px;
    box-sizing: border-box;
    min-height: 100%;
    overflow: auto;
    position: relative;
  }

  .context-menu {
    position: fixed;
    background: #fff;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
    border-radius: 4px;
    padding: 4px 0;
    z-index: 1000;

    ul {
      list-style: none;
      margin: 0;
      padding: 0;

      li {
        padding: 5px 12px;
        cursor: pointer;
        transition: all 0.3s;

        &:hover {
          background: #f3f3f3;
        }
      }
    }
  }

  .node-tooltip {
    position: fixed;
    background: #fff;
    border: 1px solid #e8e8e8;
    border-radius: 4px;
    padding: 12px;
    min-width: 200px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
    z-index: 1000;

    .tooltip-title {
      font-size: 14px;
      font-weight: 500;
      color: #333;
      margin-bottom: 8px;
      padding-bottom: 8px;
      border-bottom: 1px solid #e8e8e8;
    }

    .tooltip-section {
      margin-top: 8px;

      .section-title {
        font-size: 12px;
        color: #666;
        margin-bottom: 4px;
      }

      .section-content {
        font-size: 12px;
        color: #333;

        > div {
          margin-bottom: 2px;
          padding-left: 8px;
        }
      }

      .twoColumn {
        display: grid;
        grid-template-columns: 1fr 1fr;
        gap: 10px;
      }

      .section-empty {
        font-size: 12px;
        color: #999;
        font-style: italic;
        padding-left: 8px;
      }
    }
  }

  .node-params-tooltip {
    position: absolute;
    background: #fff;
    border: 1px solid #e8e8e8;
    border-radius: 4px;
    padding: 12px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
    z-index: 999;
    pointer-events: none;
    box-sizing: border-box;
    margin-top: 10px;

    .tooltip-title {
      font-size: 14px;
      font-weight: 500;
      color: #333;
      margin-bottom: 8px;
      padding-bottom: 8px;
      border-bottom: 1px solid #e8e8e8;
    }

    .tooltip-section {
      margin-top: 8px;

      .section-title {
        font-size: 12px;
        color: #666;
        margin-bottom: 4px;
      }

      .section-content {
        font-size: 12px;
        color: #333;

        > div {
          margin-bottom: 2px;
        }
      }
    }
  }
}
.full-screen {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 102;
  .tooltip {
    position: absolute;
    top: -7%;
    left: 50%;
    z-index: 1;
    max-width: 70%;
    transform: translateX(-50%);
    text-align: center;
    display: flex;
    flex-direction: column;
    justify-content: space-around;
    align-items: center;
    transition: all 0.2s ease-in 0.1s;
    .tooltip-text {
      width: 100%;
      overflow: hidden;
      background-color: #fff;
      box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.1);
      border-radius: 5px;
      display: grid;
      grid-template-columns: 1fr 1fr 1fr;
      grid-template-rows: 1fr;
      border: 1px solid #1890ff;
      .text-item {
        display: flex;
        flex-direction: column;
        .text-content {
          width: 100%;
          padding: 5px 10px;
          border: none;
          border-right: 1px solid #1890ff;
          // border-bottom: 1px solid #1890ff;
          white-space: nowrap; /* 防止文本换行 */
          text-overflow: ellipsis;
        }
        .text-title {
          border-bottom: 1px solid #1890ff;
          font-weight: 700;
        }
      }
    }
    .tooltip-handle {
      display: inline-block;
      position: absolute;
      background: #ffffff;
      border: 1px solid #dddddd;
      overflow: hidden;
      cursor: pointer;
      &:hover {
        background: #dddddd;
      }
      width: 30px;
      height: 20px;
      line-height: 20px;
      top: 100%;
      border-top: none;
      border-radius: 0 0 20px 20px;
      box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.1);
    }
  }

  .tooltip-show {
    top: 0;
  }
}
</style>
